package com.maxd.controller.wx;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.maxd.adapter.IgnoreSecurity;
import com.maxd.error.Result;
import com.maxd.error.ResultUtil;
import com.maxd.model.*;
import com.maxd.respository.common.CommonRepository;
import com.maxd.respository.token.TokenJpaRepository;
import com.maxd.respository.token.TokenRepository;
import com.maxd.respository.userinfo.UserJpaRepository;
import com.maxd.respository.userinfo.UserRepository;
import com.maxd.respository.word.WordJpaRepository;
import com.maxd.respository.word.WordRepository;
import com.maxd.service.message.IMessageService;
import com.maxd.service.userinfo.IUserService;
import com.maxd.service.wx.AccountService;
import com.maxd.service.wx.AccountTokenService;
import com.maxd.utils.Base64;
import com.maxd.utils.Config;
import com.maxd.utils.HttpClientUtil;
import com.maxd.utils.MessageUtil;
import com.qq.weixin.mp.aes.AesException;
import com.qq.weixin.mp.aes.WXBizMsgCrypt;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import weixin.popular.api.MenuAPI;
import weixin.popular.api.UserAPI;
import weixin.popular.bean.BaseResult;
import weixin.popular.bean.message.EventMessage;
import weixin.popular.bean.user.FollowResult;
import weixin.popular.bean.user.User;
import weixin.popular.bean.xmlmessage.XMLMessage;
import weixin.popular.bean.xmlmessage.XMLTextMessage;
import weixin.popular.support.ExpireKey;
import weixin.popular.support.expirekey.DefaultExpireKey;
import weixin.popular.util.SignatureUtil;
import weixin.popular.util.StreamUtils;
import weixin.popular.util.XMLConverUtil;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URLDecoder;
import java.nio.charset.Charset;
import java.security.AlgorithmParameters;
import java.security.Security;
import java.security.spec.AlgorithmParameterSpec;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * @author zhangd
 */
@Slf4j
@RestController
@Api(value = "微信账号管理", tags = {"微信账号管理"})
@RequestMapping("/wx")
public class WxController {
    private Logger logger = LoggerFactory.getLogger(WxController.class);
    private int number = 1;
    /**
     * 定义 AccountService
     */
    private final AccountService accountService;
    private final AccountTokenService accountTokenService;
    @Autowired
    private CommonRepository commonRepository;
    @Autowired
    private IUserService iUserService;
    @Autowired
    private UserRepository userRepository;
    @Autowired
    private WordRepository wordRepository;
    @Autowired
    private WordJpaRepository wordJpaRepository;
    @Autowired
    private UserJpaRepository userJpaRepository;
    @Autowired
    private TokenRepository tokenRepository;
    @Autowired
    private TokenJpaRepository tokenJpaRepository;
    @Autowired
    private IMessageService iMessageService;
    //重复通知过滤
    private static ExpireKey expireKey = new DefaultExpireKey();

    /**
     * 构造方法
     *
     * @param accountService AccountService
     */
    @Autowired
    public WxController(AccountService accountService, AccountTokenService accountTokenService) {
        this.accountService = accountService;
        this.accountTokenService = accountTokenService;
    }

    /**
     * 根据微信获取凭证
     *
     * @param openId 微信
     * @return BingingResultView
     */
    @PostMapping("/account/token/{openId}")
    @ApiOperation("获取token")
    public String getAccountTokenWeixin(@PathVariable("openId") String openId) {
        return accountService.getAccountTokenWeixin(openId);
    }

    @RequestMapping(value = "/saveWord", method = RequestMethod.POST)
    @ApiOperation("保存关键字")
    public Result saveWord(@RequestBody WordInfo wordInfo) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date now = new Date();
        wordInfo.setCreateAt(sdf.format(now));
        return ResultUtil.success(wordJpaRepository.save(wordInfo));
    }

    @PostMapping("/getWordList")
    @ApiOperation("保存关键字")
    public Result getWordList() {
        return ResultUtil.success(wordJpaRepository.findAll());
    }

    @PostMapping("/deleteById/{id}")
    @ApiOperation("删除关键字")
    public Result deleteById(@PathVariable Long id) {
        wordJpaRepository.deleteById(id);
        return ResultUtil.success();
    }

    /**
     * 获取openid
     *
     * @param code 微信code
     * @return openid
     */
    @GetMapping("/openId/{code:.+}/{userId}")
    @ApiOperation("根据code获取openid")
    public Result getOpenid(@PathVariable("code") String code , @PathVariable("userId")Long userId) {
        return accountService.getOpenId(code, userId);
    }

    @GetMapping("/createQr")
    @ApiOperation("用户端创建免单商品二维码")
    public Result createQr(@RequestParam String relation) {
        return accountService.createQr(relation);
    }

    @ApiOperation("微信小程序登陆")
    @RequestMapping(value = "/mp/login", method = RequestMethod.POST)
    @IgnoreSecurity
    public Result user_login(@RequestBody WXLoginVo v) {
        try {
            String appid = commonRepository.findOne(45).getValue();
            String secret = commonRepository.findOne(46).getValue();
            // 配置请求参数
            Map<String, String> param = new HashMap<>();
            param.put("appid", appid);
            param.put("secret", secret);
            param.put("js_code", v.getCode());
            param.put("grant_type", "authorization_code");
            param.put("scope", "snsapi_userinfo");
            // 发送请求
            String wxResult = HttpClientUtil.doGet("https://api.weixin.qq.com/sns/jscode2session", param);
            log.error(wxResult);
            JSONObject jsonObject = JSONObject.parseObject(wxResult);
            log.error(jsonObject.toJSONString());
            // 获取参数返回的
            Map<String, String> map = new HashMap<>();
            String session_key = jsonObject.get("session_key").toString();
            String open_id = jsonObject.get("openid").toString();
            //unionid
            /*JSONObject userInfo = getUserInfo(v.getEncryptedData(), session_key, v.getIv());
            System.out.println(userInfo.toString());
            System.out.println(userInfo.toJSONString());*/
            CommonInfo one = commonRepository.findOne(236);
            if("是".equals(one.getValue())){
                UserInfo userByWxId = userRepository.getUserByMpId(open_id);
                if(userByWxId!=null && StringUtils.isNotEmpty(userByWxId.getPhone())){
                    map.put("flag","0");
                }else{
                    map.put("flag","1");
                }
            }else{
                map.put("flag","0");
            }
            if (jsonObject.get("unionid")!=null){
                map.put("unionid", jsonObject.get("unionid").toString());
            }else{
                map.put("unionid", "-1");
            }
            // 封装返回小程序
            map.put("session_key", session_key);
            map.put("open_id", open_id);
            return ResultUtil.success(map);
//            if (StringUtils.isNotBlank(unionid)) {
//                UserInfo one = userRepository.getUserByUnionid(unionid);
//                if (one != null) {
//                    return getResult(userJpaRepository.save(one));
//                } else {
//
//                    return ResultUtil.error(-1,open_id);
//                }
//
//            } else if (StringUtils.isNotBlank(open_id)) {
//                UserInfo one = userRepository.getUserByMpId(open_id);
//                if (one != null) {
//                    return getResult(userJpaRepository.save(one));
//                } else {
//                    return ResultUtil.error(-1,open_id);
//                }
//            } else {
//
//            }
        } catch (Exception e) {
            log.error("微信登录异常："+e.getMessage(),e);
            return ResultUtil.error(-1, "登录失败！");
        }
    }


    @ApiOperation("解密手机号")
    @RequestMapping(value = "/selectPhone", method = RequestMethod.POST)
    @IgnoreSecurity
    public Result getPhoneNumberBeanS5(String decryptData, String key, String iv) {
        String decrypt = null;
        System.err.println("decryptData:" + decryptData + "    key:" + key + "    iv:" + iv);
        try {
            byte[] encrypData = org.apache.commons.codec.binary.Base64.decodeBase64(decryptData);
            byte[] ivData = org.apache.commons.codec.binary.Base64.decodeBase64(iv);
            byte[] sKey = org.apache.commons.codec.binary.Base64.decodeBase64(key);
            decrypt = decryptS5(sKey, ivData, encrypData);
//            JSONObject jsonObject= JSON.parseObject(decrypt);
//            String data = jsonObject.getString("data");
        } catch (Exception e) {
            e.printStackTrace();
            return ResultUtil.error(-1, "获取手机号失败");
        }
        System.err.println(decrypt);
        if (StringUtils.isEmpty(decrypt)) {
            return ResultUtil.error(-1, "获取手机号失败");
        }
        return ResultUtil.success(JSONObject.parseObject(decrypt));
    }


    private static String decryptS5(byte[] key, byte[] iv, byte[] encData) throws Exception {
        AlgorithmParameterSpec ivSpec = new IvParameterSpec(iv);
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
        cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
        //解析解密后的字符串
        return new String(cipher.doFinal(encData), "UTF-8");
    }



    /**
     * 解密用户敏感数据获取用户信息
     * @param sessionKey 数据进行加密签名的密钥
     * @param encryptedData 包括敏感数据在内的完整用户信息的加密数据
     * @param iv 加密算法的初始向量
     */
    public JSONObject getUserInfo(String encryptedData,String sessionKey,String iv){
        // 被加密的数据
        byte[] dataByte = Base64.decode(encryptedData);
        // 加密秘钥
        byte[] keyByte = Base64.decode(sessionKey);
        // 偏移量
        byte[] ivByte = Base64.decode(iv);
        try {
            // 如果密钥不足16位，那么就补足.  这个if 中的内容很重要
            int base = 16;
            if (keyByte.length % base != 0) {
                int groups = keyByte.length / base + (keyByte.length % base != 0 ? 1 : 0);
                byte[] temp = new byte[groups * base];
                Arrays.fill(temp, (byte) 0);
                System.arraycopy(keyByte, 0, temp, 0, keyByte.length);
                keyByte = temp;
            }
            // 初始化
            Security.addProvider(new BouncyCastleProvider());
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding","BC");
            SecretKeySpec spec = new SecretKeySpec(keyByte, "AES");
            AlgorithmParameters parameters = AlgorithmParameters.getInstance("AES");
            parameters.init(new IvParameterSpec(ivByte));
            cipher.init(Cipher.DECRYPT_MODE, spec, parameters);// 初始化
            byte[] resultByte = cipher.doFinal(dataByte);
            if (null != resultByte && resultByte.length > 0) {
                String result = new String(resultByte, "UTF-8");
                return JSONObject.parseObject(result);
            }
        }catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }


        private Result getResult(UserInfo userInfo) {
        Token byUserIds = tokenRepository.findByUserId(String.valueOf(userInfo.getId()));
        if (byUserIds == null) {
            String uuid = UUID.randomUUID().toString();
            Token token = new Token();
            token.setUserId(String.valueOf(userInfo.getId()));
            token.setUuid(uuid);
            Token save = tokenJpaRepository.save(token);
            return ResultUtil.success(save);
        }
        return ResultUtil.success(byUserIds);
    }

    @GetMapping("/mpCreateQr")
    @ApiOperation("小程序创建二维码")
    public Result mpCreateQr(@RequestParam String value) {
        return accountService.createQr(value);
    }

    //非线程安全实现，需要修改
    static class SingleByUserId {
        static Map<String, Object> map = new HashMap<>();

        static Object getById(String userId) {
            if (map.get(userId) != null) {
                return map.get(userId);
            } else {
                Object object = new Object();
                map.put(userId, object);
                return object;
            }
        }
    }

    @RequestMapping(value = "/sqx/sendMsg", method = {RequestMethod.GET, RequestMethod.POST})
    @ApiOperation("回复公众号消息")
    @ResponseBody
    public Result sendMsg(String content,Long id,HttpServletResponse response) throws IOException {
        MessageInfo messageInfo = iMessageService.selectById(id);
        ServletOutputStream outputStream = response.getOutputStream();
        WXBizMsgCrypt wxBizMsgCrypt=(WXBizMsgCrypt)JSON.parse(messageInfo.getWxBizMsgCrypt());
        //创建回复
        XMLMessage xmlTextMessage = new XMLTextMessage(
                messageInfo.getFromUserName(),
                messageInfo.getToUserName(),
                content);
        //回复
        xmlTextMessage.outputStreamWrite(outputStream, wxBizMsgCrypt);
        return ResultUtil.success();
    }



    private void sendSearch(ServletOutputStream outputStream, WXBizMsgCrypt wxBizMsgCrypt, EventMessage eventMessage, String content) {
        //后台服务域名配置
        CommonInfo yuming = commonRepository.findOne(19);
        //注册邀请码
        CommonInfo yaoqingma = commonRepository.findOne(4);
        User wx = accountService.getUserInfo(eventMessage.getFromUserName());
        if (wx.getNickname() != null) {
            String mycontent1 = Config.menuText1(wx.getNickname(),yuming.getValue() + "/#/pages/zysc/index/search?keyword=" + content + "&invitation=" + yaoqingma.getValue());
            //创建回复
            XMLMessage xmlTextMessage = new XMLTextMessage(
                    eventMessage.getFromUserName(),
                    eventMessage.getToUserName(),
                    mycontent1);
            //回复
            xmlTextMessage.outputStreamWrite(outputStream, wxBizMsgCrypt);
        } else {
            String mycontent1 = Config.menuText1("亲", yuming.getValue() + "/#/pages/zysc/index/search?keyword=" + content + "&invitation=" + yaoqingma.getValue() );
            //创建回复
            XMLMessage xmlTextMessage = new XMLTextMessage(
                    eventMessage.getFromUserName(),
                    eventMessage.getToUserName(),
                    mycontent1);
            //回复
            xmlTextMessage.outputStreamWrite(outputStream, wxBizMsgCrypt);
        }
    }

    private void sendTwoSearch(ServletOutputStream outputStream, WXBizMsgCrypt wxBizMsgCrypt, EventMessage eventMessage, String content) {
        String mycontent1 = "未能通过链接找到您需要的商品\n" +
                "-------------------------------\n" +
                "☞ 请发送【商品标题】至公众号查询 >>\n" +
                "-------------------------------\n" +
                "人工客服，请点右下角";
        //创建回复
        XMLMessage xmlTextMessage = new XMLTextMessage(
                eventMessage.getFromUserName(),
                eventMessage.getToUserName(),
                mycontent1);
        //回复
        xmlTextMessage.outputStreamWrite(outputStream, wxBizMsgCrypt);
    }

    /**
     * 数据流输出
     */
    private void outputStreamWrite(OutputStream outputStream, String text) {
        try {
            outputStream.write(text.getBytes("utf-8"));
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    /**
     * token
     * 获取token
     *
     * @return openid
     */
    @GetMapping("/token")
    @ApiOperation("管理平台获取token")
    public String getToken() {
        return accountTokenService.getWxToken();
    }


//    @GetMapping("sendMsg")
//    @ApiOperation("消息推送")
//    public void sendMessage(@RequestParam String title, @RequestParam String content,@RequestParam String client) {
//        pushToSingle(title, content,client);
//    }


    /**
     * token
     * 获取菜单
     *
     * @return openid
     */
    @GetMapping("/menu")
    @ApiOperation("管理平台获取菜单")
    public weixin.popular.bean.menu.Menu getMenu() {
        return MenuAPI.menuGet(accountTokenService.getWxToken());
    }

    /**
     * token
     * 创建菜单
     *
     * @return openid
     */
    @PostMapping("/create")
    @ApiOperation("管理平台创建菜单")
    public Result createMenu(@RequestBody Menu menu) {
        String s = JSON.toJSONString(menu);
        BaseResult baseResult = MenuAPI.menuCreate(accountTokenService.getWxToken(), s);
        return ResultUtil.success(baseResult);
    }

    /**
     * 检查是否关注公众号
     *
     * @param openid openid
     * @return boolean
     */
    @GetMapping("/follow/{openId:.+}")
    @ApiOperation("用户端检查是否关注公众号")
    public boolean checkIsFollow(@PathVariable("openId") String openid) {
//        log.info("判断用户是否关注：openid={}", openid);
        return accountService.checkIsFollow(openid);
    }

    /**
     * 获取openid
     * 获取用户信息
     *
     * @param openId openId
     * @return openid
     */
    @GetMapping("/userInfo/{openId:.+}")
    @ApiOperation("用户端通过openid获取微信用户信息")
    public User getUserInfo(@PathVariable("openId") String openId) {
        return accountService.getUserInfo(openId);
    }

    /**
     * 获取签名
     * 监测js签名
     *
     * @param url request的url
     * @return String
     */
    @GetMapping("/js/sign")
    @ApiOperation("用户端获取js签名")
    public String getJSSign(@RequestParam String url) {
        return accountService.getJSSign(url);
    }

    @GetMapping("/users")
    @ApiOperation("获取关注公众号用户列表")
    public FollowResult getUserList() {
        return UserAPI.userGet(accountTokenService.getWxToken(), null);
    }

    @GetMapping("/getUserTotal")
    @ApiOperation("获取关注公众号用户总数")
    public Result getUserTotal() {
        return ResultUtil.success(UserAPI.userGet(accountTokenService.getWxToken(), null).getTotal());
    }

//    @GetMapping("/saveUser")
//    public Result saveUser() {
//        FollowResult followResult = UserAPI.userGet(accountTokenService.getWxToken(), null);
//        String[] openid = followResult.getData().getOpenid();
//        for (String i : openid) {
//            Result result = iUserService.loginByOpenId(i);
//        }
//        return ResultUtil.success();
//    }

    /**
     * 微信公众号消息管理
     *
     * @return openid
     */
    @RequestMapping(value = "/sqx/connect", method = {RequestMethod.GET, RequestMethod.POST})
    @ApiOperation("在公众号平台配置 微信公众号消息")
    @ResponseBody
    public void connectWeixinsqx(HttpServletRequest request, HttpServletResponse response) throws IOException {
        //公众号Token
        CommonInfo token = commonRepository.findOne(16);
        //公众号EncodingAESKey
        CommonInfo EncodingAESKey = commonRepository.findOne(17);
        //微信APPID
        CommonInfo appid = commonRepository.findOne(5);
        //后台服务域名配置
        CommonInfo yuming = commonRepository.findOne(19);
        //邀请码
        CommonInfo one = commonRepository.findOne(4);
        //后台服务名称
        CommonInfo name = commonRepository.findOne(12);
        ServletInputStream inputStream = request.getInputStream();
        ServletOutputStream outputStream = response.getOutputStream();
        String signature = request.getParameter("signature");
        String timestamp = request.getParameter("timestamp");
        String nonce = request.getParameter("nonce");
        String echostr = request.getParameter("echostr");
        //加密模式
        String encrypt_type = request.getParameter("encrypt_type");
        String msg_signature = request.getParameter("msg_signature");
        WXBizMsgCrypt wxBizMsgCrypt = null;
        //加密方式
        boolean isAes = "aes".equals(encrypt_type);
        if (isAes) {
            try {
                wxBizMsgCrypt = new WXBizMsgCrypt(accountTokenService.getWxToken(), EncodingAESKey.getValue() != null ? EncodingAESKey.getValue() : Config.encodingAesKey, appid.getValue() != null ? appid.getValue() : Config.appId);
            } catch (AesException e) {
                e.printStackTrace();
            }
        }

        //首次请求申请验证,返回echostr
        if (isAes && echostr != null) {
            try {
                echostr = URLDecoder.decode(echostr, "utf-8");
                assert wxBizMsgCrypt != null;
                String echostr_decrypt = wxBizMsgCrypt.verifyUrl(msg_signature, timestamp, nonce, echostr);
                outputStreamWrite(outputStream, echostr_decrypt);
                return;
            } catch (AesException e) {
                e.printStackTrace();
            }
        } else if (echostr != null) {
            outputStreamWrite(outputStream, echostr);
            return;
        }

        EventMessage eventMessage = null;
        if (isAes) {
            try {
                //获取XML数据（含加密参数）
                String postData = StreamUtils.copyToString(inputStream, Charset.forName("utf-8"));
                //解密XML 数据
                assert wxBizMsgCrypt != null;
                String xmlData = wxBizMsgCrypt.decryptMsg(msg_signature, timestamp, nonce, postData);
                //XML 转换为bean 对象
                eventMessage = XMLConverUtil.convertToObject(EventMessage.class, xmlData);
            } catch (AesException e) {
                e.printStackTrace();
            }
        } else {
            if (signature == null) {
                System.out.println("The request signature is null");
                return;
            }
            //验证请求签名
            if (!signature.equals(SignatureUtil.generateEventMessageSignature(token.getValue() != null ? token.getValue() : Config.token, timestamp, nonce))) {
                System.out.println("The request signature is invalid");
                return;
            }

            if (inputStream != null) {
                //XML 转换为bean 对象
                eventMessage = XMLConverUtil.convertToObject(EventMessage.class, inputStream);
            }
        }
        String fromUserName = eventMessage.getFromUserName();
        String key = fromUserName + "__"
                + eventMessage.getToUserName() + "__"
                + eventMessage.getMsgId() + "__"
                + eventMessage.getCreateTime();
        if (expireKey.exists(key)) {
            //重复通知不作处理
            return;
        } else {
            expireKey.add(key);
        }

        String event = eventMessage.getEvent();
        String msgType1 = eventMessage.getMsgType();
        String eventKey = eventMessage.getEventKey();
        logger.error("getEvent----" + event);
        logger.error("getMsgType----" + msgType1);
        logger.error("eventKey----" + eventKey);
        //判断请求是否事件类型 event 用户关注公众号事件
        if (!MessageUtil.EVENT_SUB.equals(event)) {
            if (StringUtils.isNotBlank(eventMessage.getContent())) {
                String content = eventMessage.getContent().replaceAll("\\s*", "");
                logger.error("receiver content is ----" + content);
                logger.error("getToUserName" + eventMessage.getToUserName());
                logger.error("getFromUserName" + fromUserName);
                //关键字回复
                WordInfo one1 = wordRepository.findOne(content);
                if (one1 != null) {
                    //创建回复
                    XMLMessage xmlTextMessage = new XMLTextMessage(
                            fromUserName,
                            eventMessage.getToUserName(),
                            one1.getValue());
                    //回复
                    xmlTextMessage.outputStreamWrite(outputStream, wxBizMsgCrypt);
                }
            }
        }
    }
}
